package hardware;

import java.util.LinkedList;
import java.util.List;

public class Decode {

	static LinkedList<Instruction> instructions = new LinkedList<Instruction>();

	/**
	 * Renames the destination in the list of instructions in decode and gets
	 * the renamed name of the source registers.
	 * 
	 * @param ins
	 *            list of instructions i need to search in.
	 * @return The new list of instructions after renaming.
	 */
	public static List<Instruction> decoder(List<Instruction> ins) {
		Register temp;
		for (int i = 0; i < ins.size(); i++) {
			ins.get(i).setRegB(RegisterMap.getRenamed(ins.get(i).getRegB()));
			if (ins.get(i).getRegC() != null)
				ins.get(i)
						.setRegC(RegisterMap.getRenamed(ins.get(i).getRegC()));

			if (!ins.get(i).getType().equals(Instruction.STORE_WORD)
					&& !ins.get(i).getType()
							.equals(Instruction.BRANCH_IF_EQUAL)
					&& !ins.get(i).getType().equals(Instruction.JUMP)
					&& !ins.get(i).getType().equals(Instruction.RETURN)) {
				temp = RegisterMap.rename(ins.get(i).getRegA());
				if (temp != null) {
					ins.get(i).setRegA(temp);
					ins.get(i).getRegA().ready = false;
				} else {
					break;
				}
			}
			if(ins.get(i).getType().equals(Instruction.RETURN)){
				ins.get(i).setRegA(RegisterMap.getRenamed(ins.get(i).getRegA()));
			}
			
		}
		return ins;
	}

	/**
	 * It send the instructions to dispatch if there is a place in rob and
	 * reservation station and in dispatch it self.
	 * 
	 * @param currentTime
	 *            the current time at which the operation is performed.
	 */
	public static void decode(long currentTime) {
		Instruction instruction;
		if (Dispatch.instructions.isEmpty()) {
			for (int i = 0; i < instructions.size(); i++) {
				instruction = instructions.get(i);
				if (checkIfRenamed(instruction)
						&& Processor.reserveInReservationStation(instruction)
						&& !Processor.ROBIsFull()
						&& Dispatch.instructions.size() < Processor.m) {
					// Check if there's a place in the ROB and in the
					// reservation
					// station
					// Reserve a reservation station for dispatch stage and move
					// the
					// instruction to the next stage(Dispatch)
					instruction.setDecEndTime(currentTime);
					Dispatch.dispatchNew(instructions.remove(i),
							instruction.getOriginalLogical(), currentTime + 1);
					i--;
				} else {
					break;
				}
			}
		}

		/**
		 * Check if there's no instruction in decode stage at the current time
		 * If there are instructions in the instruction buffer get (m)
		 * instructions to decode stage
		 */
		if (instructions.isEmpty()) {
			for (int k = 0; k < Processor.m
					&& (!Processor.instructionBuffer.isEmpty()); k++) {
				instruction = Processor.instructionBuffer.removeFirst();
				if (instruction != null) {
					instruction.setDecStartTime(currentTime);
					instructions.add(instruction);
				}
			}
			decoder(instructions);
		}
	}

	/**
	 * checks that the destination register is renamed to handle if there is no
	 * more free physical registers it will stall.
	 * 
	 * @param ins
	 *            the instruction that i want to check if it's renamed or not.
	 * @return true if it's renamed.
	 */
	private static boolean checkIfRenamed(Instruction ins) {
		String type = ins.getType();
		Register r = ins.getRegA();
		boolean notRenamingType = (!type.equals(Instruction.STORE_WORD)
				&& !type.equals(Instruction.BRANCH_IF_EQUAL)
				&& !type.equals(Instruction.JUMP) && !type
				.equals(Instruction.RETURN));
		boolean isLogicalRegister = (r.equals(Processor.reg0)
				|| r.equals(Processor.reg1) || r.equals(Processor.reg2)
				|| r.equals(Processor.reg3) || r.equals(Processor.reg4)
				|| r.equals(Processor.reg5) || r.equals(Processor.reg6) || r
				.equals(Processor.reg7));
		if (notRenamingType && isLogicalRegister)
			return false;
		else
			return true;

	}
}
